package com.zoe.onelink.feign.autoconfigure;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.client.ClientException;
import com.zoe.onelink.auth.property.OnelinkAuthProperties;
import com.zoe.onelink.core.exception.ExceptionResolver;
import com.zoe.onelink.core.exception.interceptor.WebExceptionInterceptor;
import com.zoe.onelink.feign.auth.aspect.FeignInnerApiAspect;
import com.zoe.onelink.feign.auth.token.FeignTokenEncryptor;
import com.zoe.onelink.feign.auth.token.FeignTokenValidator;
import com.zoe.onelink.feign.auth.token.impl.FeignAesAuthTokenEncryptor;
import com.zoe.onelink.feign.auth.token.impl.FeignAuthTokenValidator;
import com.zoe.onelink.feign.auth.token.impl.FeignFixedTokenEncryptor;
import com.zoe.onelink.feign.contract.OnelinkFeignContract;
import com.zoe.onelink.feign.decode.FeignApiResultDecoder;
import com.zoe.onelink.feign.decode.OnelinkFeignErrorDecoder;
import com.zoe.onelink.feign.encode.SpringMultipartEncoder;
import com.zoe.onelink.feign.interceptor.FeignExceptionInterceptor;
import com.zoe.onelink.feign.interceptor.FeignRequestInterceptor;
import com.zoe.onelink.feign.property.OnelinkFeignProperties;
import com.zoe.onelink.feign.resolver.FeignExceptionResolver;
import com.zoe.onelink.util.OnelinkContextHolder;
import feign.Contract;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.optionals.OptionalDecoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-12-16
 */
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignClientsConfiguration.class)
@EnableConfigurationProperties(OnelinkFeignProperties.class)
public class OnelinkFeignAutoConfiguration {

    @Value("${onelink.feign.validator.token:tkP1JAXDABwmP9pwC1m0}")
    public String innerToken;

    @Bean
    public RequestInterceptor feignRequestInterceptor(ObjectProvider<FeignTokenEncryptor> feignTokenEncryptors,
                                                      OnelinkFeignProperties onelinkFeignProperties) {
        return new FeignRequestInterceptor(feignTokenEncryptors.getIfAvailable(), onelinkFeignProperties.getRequest());
    }

    @Bean
    @Qualifier
    public WebExceptionInterceptor feignExceptionInterceptor(HttpServletRequest request, HttpServletResponse response) {
        return new FeignExceptionInterceptor(request, response);
    }

    @Bean
    public Contract onelinkFeignContract() {
        return new OnelinkFeignContract();
    }


    @Bean
    public Decoder feignResponseDecoder(ObjectMapper objectMapper) {
        return new OptionalDecoder(new ResponseEntityDecoder(new FeignApiResultDecoder(objectMapper)));
    }

    @Bean
    public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringMultipartEncoder(new SpringEncoder(messageConverters));
    }

    @Bean
    public ErrorDecoder onelinkFeignErrorDecoder() {
        return new OnelinkFeignErrorDecoder();
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public FeignTokenEncryptor feignFixedTokenValidator() {
        return new FeignFixedTokenEncryptor(this.innerToken);
    }

    @Bean
    public FeignInnerApiAspect feignInnerApiAspect(HttpServletRequest request, ObjectProvider<FeignTokenValidator> feignTokenValidators) {
        return new FeignInnerApiAspect(request, feignTokenValidators.getIfAvailable());
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ClientException.class, ExceptionResolver.class})
    static class FeignExternalAutoConfiguration {

        @Bean
        public FeignExceptionResolver feignClientExceptionProcessor() {
            return new FeignExceptionResolver();
        }

    }

    @Configuration
    @ConditionalOnClass({OnelinkContextHolder.class, OnelinkAuthProperties.class})
    static class FeignAuthTokenAutoConfiguration {

        @Bean
        @ConditionalOnMissingBean(FeignTokenEncryptor.class)
        public FeignTokenEncryptor feignTokenGenerator(OnelinkAuthProperties onelinkAuthProperties) {
            return new FeignAesAuthTokenEncryptor(onelinkAuthProperties.getTokenSecretKey());
        }

        @Bean
        @Primary
        @ConditionalOnMissingBean(FeignAuthTokenValidator.class)
        @ConditionalOnProperty(name = "onelink.feign.auth.validator.enabled", matchIfMissing = true)
        public FeignTokenValidator feignAuthTokenValidator(FeignTokenEncryptor feignTokenEncryptor) {
            return new FeignAuthTokenValidator(feignTokenEncryptor);
        }

    }


}
